/**
 * @author Cator Vee  hi@catorv.com
 */
import { isString, mixin, nop, resolveNames } from './utils'
import { buildUrl, queryString } from './utils/url'
import { storage } from './lib/data'
import dva from 'dva'
import api from './lib/api'
import createLoading from 'dva-loading'
import env from './lib/env'

const KEY_CONTEXT = 'app-context'

export let app = global.app = {
  context: storage.get(KEY_CONTEXT) || {},
  updateContext: (context) => {
    mixin(app.context, context, true)
    storage.set(KEY_CONTEXT, app.context)
    dispatch('context/fromUpdateContext', app.context)
  },
  deleteContext: (name) => {
    delete app.context[name]
    storage.set(KEY_CONTEXT, app.context)
    dispatch('context/fromUpdateContext', app.context)
  }
}

let actions = []
let _dispatch

export function dispatch (type, payload) {
  if (_dispatch) {
    return _dispatch({type, payload})
  }
  actions.push({type, payload})
}

export const vee = window.vee = {
  ui: {
    alert: options => {
      if (isString(options)) {
        options = {content: options}
      }
      return vee.notification.send('alert', options).then(action => action.payload)
    },
    toast: options => {
      if (isString(options)) {
        options = {content: options}
      }
      return vee.notification.send('toast', options)
    }
  },
  notification: {
    send: (name, info) => {
      return dispatch('notifications/sendAndWait', {...info, name: name})
    },
    receive: (id) => {
      dispatch('notifications/receive', id)
    },
    findFirst: (notifications, name) => {
      for (let i = 0; i < notifications.length; i++) {
        if (notifications[i].name === name) {
          return notifications[i]
        }
      }
      return null
    },
    findLast: (notifications, name) => {
      for (let i = notifications.length - 1; i >= 0; i--) {
        if (notifications[i].name === name) {
          return notifications[i]
        }
      }
      return null
    }
  }
}

const reduxConnect = require('react-redux').connect

export function connect (mapStateToProps, mapDispatchToProps, mergeProps, options) {
  let _mapStateToProps = function (state) {
    return {
      context: state.context || {},
      notifications: state.notifications || {},
      loading: state.loading || {},
      wechat: state.wechat || {},
      clientId: env.clientId,
      ...(mapStateToProps && mapStateToProps(state))
    }
  }
  return reduxConnect(_mapStateToProps, mapDispatchToProps, mergeProps, options)
}

export default function init (options) {
  const config = app.config = resolveNames(options.config || {})
  delete options.config
  const fake = config.fake
  delete config.fake
  app.apis = api.generate(config.apis, config.apirhs)
  app.apis.handleError = api.handleError
  delete config.apis
  delete config.apirhs

  let context = app.context
  const query = queryString()
  let needsReload = query.fake || query.cmd
  if (query.cmd) {
    switch (query.cmd) {
      case 'clear':
        storage.clear()
        break
    }
    delete query.cmd
  }
  if (query.fake) {
    const fakeContext = fake[query.fake] || query.fake
    if (fakeContext) {
      context = fakeContext
    }
    delete query.fake
  }
  if (needsReload) {
    window.location.href = buildUrl(query)
    return
  }
  app.updateContext(context)

  app = mixin(dva({
    ...options,
    initialState: {...options.initialState}
  }), app)
  app.preprocessing = app.postprocessing = Promise.resolve()
  const oldAppStart = app.start.bind(app)
  app.start = function (container) {
    app.preprocessing.then(dvaStart).catch(nop)

    function dvaStart () {
      app.use(createLoading())

      app.model(require('./models/Context').default)
      // 在H5项目中由APP调用
      // app.model(require('./models/Notifications').default)

      oldAppStart(container)
      _dispatch = app._store.dispatch
      actions.forEach(action => _dispatch(action))
      actions = null
    }
  }
  return app
}
